﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using Microsoft.Win32;

namespace WirelessMigrator
{
    class Program
    {
        const string ProfilesFormatter = "wlan {0} {1}";
        const string cShow = "show profiles";
        const string cExport = "export profile";
        const string cImport = "add profile";
        const string cExportParams = "name=\"{0}\" Folder=\"{1}\"";
        const string cImportParams = "filename=\"{0}\\{1}\"";
        const string fName = "RestoreWireless";
        const string fExt = ".wnb";
        const string ProperName = "WirelessMigratorFile";
        const string tNameFormat = "Wireless Network Connection-{0}.xml";
        const string sArg = "/init";
        const string sRem = "/remove";
        const string IconPath = "1";
        static byte[] stub = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };

        
        static void Main(string[] args)
        {
            
            bool isSetup = (Array.Find(args, ar => ar.ToLower() == sArg) != null);
            bool isRemove = (Array.Find(args, ar => ar.ToLower() == sRem) != null);

            CheckAndSetup(isSetup, isRemove);

            if (isRemove)
                Console.WriteLine("Application fully uninstalled from your computer.");
            

            if (isSetup | isRemove)
                return;
            
            Console.WriteLine("This application saves and restores wireless network profiles, found of your computer.\n\nTo store and backup wireless network profiles, run BackupWireless.exe\nTo restore saved wireless network profiles, run RestoreWireless.wnb\n\n2008, Tamir Khason\nFor more information, visit my blog at http://blogs.microsoft.co.il/blogs/tamir/");

            if (args.Length > 0)
            {
                if (!File.Exists(args[0]))
                {
                    Console.WriteLine("Wireless networks archive file \"{0}\" cannot be found...",args[0]);
                    return;
                }
                Console.WriteLine("Please wait while restoring wireless networks...");
                List<byte[]> lans = RetriveThem(args[0]);
                Console.Write("[");
                Console.CursorLeft = lans.Count + 1;
                Console.Write("]");
                Console.CursorLeft = 1;
                RestoreThem(lans);
                Console.WriteLine("\nWe done. \n\nPress any key to exit");
                Console.ReadKey();
            }
            else
            {
                Console.WriteLine("Please wait while saving wireless networks, found on your computer...");
                List<string> lans = EnumThem();
                Console.Write("[");
                Console.CursorLeft = lans.Count+1;
                Console.Write("]");
                Console.CursorLeft = 1;
                string tName = SaveThem(lans);
                Console.WriteLine("\nWe done. To restore wireless networks, run {0}\n\nPress any key to exit",tName);
                Console.ReadKey();
            }

        }

        static void CheckAndSetup(bool setup, bool remove)
        {
            if ((Registry.ClassesRoot.OpenSubKey(fExt) == null | remove) && !Helper.IsAdmin())
            {
                //first time need elevation
                ProcessStartInfo psi = new ProcessStartInfo();
                psi.UseShellExecute = true;
                psi.CreateNoWindow = true;
                psi.Arguments = remove ? sRem : sArg;
                psi.WorkingDirectory = Environment.CurrentDirectory;
                psi.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
                psi.Verb = "runas";
                Process p = Process.Start(psi);
                p.WaitForExit();
                return;
            }

            if (remove)
            {
                if (Registry.ClassesRoot.OpenSubKey(fExt) != null)
                {
                    Registry.ClassesRoot.DeleteSubKey(fExt);
                }
                if (Registry.ClassesRoot.OpenSubKey(ProperName) != null)
                {
                    Registry.ClassesRoot.DeleteSubKeyTree(ProperName);
                }
            }
            if (setup)
            {
                if (Registry.ClassesRoot.OpenSubKey(fExt) != null)
                {
                    Registry.ClassesRoot.DeleteSubKey(fExt);
                }
                using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(fExt))
                {
                    key.SetValue(string.Empty, ProperName);
                    key.Close();
                }
                if (Registry.ClassesRoot.OpenSubKey(ProperName) != null)
                {
                    Registry.ClassesRoot.DeleteSubKeyTree(ProperName);
                }
                using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(ProperName))
                {
                    key.SetValue(string.Empty, System.Reflection.Assembly.GetExecutingAssembly().Location);
                    key.Close();
                }
                using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(string.Format("{0}\\DefaultIcon", ProperName)))
                {
                    key.SetValue(string.Empty, string.Format("{0},{1}", System.Reflection.Assembly.GetExecutingAssembly().Location, IconPath));
                    key.Close();
                }
                using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(string.Format("{0}\\Shell\\Restore", ProperName)))
                {
                    using (RegistryKey skey = key.CreateSubKey("Command"))
                    {
                        skey.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", System.Reflection.Assembly.GetExecutingAssembly().Location));
                        skey.Close();
                    }
                    key.Close();
                }
            }
            
            
        }

        static List<string> EnumThem()
        {
            List<string> res = new List<string>();
            ProcessStartInfo psi = new ProcessStartInfo("netsh");
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;

            //show;
            psi.Arguments = string.Format(ProfilesFormatter, cShow, string.Empty);

            using (Process p = Process.Start(psi))
            {
                
                StreamReader sr = p.StandardOutput;

                //parse
                string ln;
                while ((ln = sr.ReadLine()) != null)
                {
                    string[] nm = ln.Split(':');
                    if (nm.Length > 1)
                    {
                        string n = nm[1].Trim();
                        if (n != string.Empty)
                            res.Add(n);
                    }
                }

            }

            return res;
        }

        static List<byte[]> RetriveThem(string path)
        {
            byte[] dta = File.ReadAllBytes(path);
            byte[][] data = Helper.SplitByteArrayByPattern(dta, stub);

            List<byte[]> res = new List<byte[]>(data.Length);
            for (int i = 0; i < data.Length; i++)
            {
                res.Add(data[i]);
            }
            return res;
        }

        static byte[] SaveOne(string name)
        { 
            ProcessStartInfo psi = new ProcessStartInfo("netsh");
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;

            psi.Arguments = string.Format(ProfilesFormatter, cExport, string.Format(cExportParams,name,Environment.CurrentDirectory));

            using (Process p = Process.Start(psi))
            {
                p.WaitForExit();    
            }
            

            string tName = string.Format(tNameFormat, name.Length > 10?name.Substring(0,10):name);
            byte[] ret = Helper.Compress(File.ReadAllBytes(tName));
            File.Delete(tName);
            return ret;
        }

        static string SaveThem(List<string> lans)
        {
            List<byte[]> bytes = new List<byte[]>();
            for (int i = 0; i < lans.Count; i++)
            {
                bytes.Add(SaveOne(lans[i]));
                Console.CursorLeft = i + 1;
                Console.Write("*");
            }

            string tName = string.Concat(fName, fExt);
            int n = 0;
            if(File.Exists(tName))
            {
                Console.WriteLine("\n\nWireless networks archive file \"{0}\" already exists.\nOld archive saved as {0}.old", tName);
                File.WriteAllBytes(string.Format("{0}.old", tName), File.ReadAllBytes(tName));
            }
            using (FileStream fs = File.Create(tName))
            {
                for (int i = 0; i < bytes.Count; i++)
                {
                    fs.Write(bytes[i], 0, bytes[i].Length);
                    fs.Write(stub, 0, stub.Length);
                }
            }

            return tName;
        }

        static void RestoreOne(byte[] data, string name)
        {
            File.WriteAllBytes(name, Helper.Decompress(data));

            ProcessStartInfo psi = new ProcessStartInfo("netsh");
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;

            psi.Arguments = string.Format(ProfilesFormatter, cImport, string.Format(cImportParams, Environment.CurrentDirectory, name));

            using (Process p = Process.Start(psi))
            {
                p.WaitForExit();
            }

            File.Delete(name);
        }

        static void RestoreThem(List<byte[]> lans)
        {
            for (int i = 0; i < lans.Count; i++)
            {
                RestoreOne(lans[i],string.Format("{0}.xml",i));
                Console.CursorLeft = i + 1;
                Console.Write("*");
            }
        }
    }
}
